# encoding: utf-8
"""
@author: Dirac
@file: account.py
@time: 2020/7/29 21:10
@desc:
"""
import random

from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator

from django_redis import get_redis_connection

from utils import encrypt
from utils.tencent import sms
from web import models
from web.forms.bootstrap import BootStrapForm


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    # 验证顺序为此处字段的排列顺序
    pass_word = forms.CharField(label='密码',
                                min_length=8,
                                max_length=64,
                                error_messages={
                                    'min-length': '密码长度不能小于8个字符',
                                    'max-length': '密码长度不能大于64个字符',
                                },
                                widget=forms.PasswordInput()
                                )

    confirm_pass_word = forms.CharField(label='重复密码', widget=forms.PasswordInput())

    mobile_phone = forms.CharField(label='手机号',
                                   validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$',
                                                              '手机号格式错误'), ])

    code = forms.CharField(label='验证码')

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'pass_word', 'confirm_pass_word', 'mobile_phone', 'code']

    def clean_username(self):
        username = self.cleaned_data.get('username')
        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            self.add_error('username', '用户名已存在。')
        return username

    def clean_email(self):
        email = self.cleaned_data.get('email')
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            self.add_error('email', '邮箱已存在。')
        return email

    def clean_pass_word(self):
        pass_word = self.cleaned_data.get('pass_word')
        # 返回加密后的密码
        return encrypt.md5(pass_word)

    def clean_confirm_pass_word(self):
        pass_word = self.cleaned_data.get('pass_word')
        confirm_pass_word = encrypt.md5(self.cleaned_data.get('confirm_pass_word'))  # pass_word之前使用了md5加密，因此此处也需要加密以便比较
        if pass_word != confirm_pass_word:
            self.add_error('pass_word', '密码不一致。')

        return confirm_pass_word

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data.get('mobile_phone')

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists();
        if exists:
            self.add_error('mobile_phone', '手机号已注册。')

        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data.get('code')
        mobile_phone = self.cleaned_data['mobile_phone']

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)

        if not redis_code:
            self.add_error('code', '验证码失效或未发送')
            return code

        redis_str_code = redis_code.decode('utf-8')
        print(redis_str_code)
        print(code)

        if code.strip() != redis_str_code:
            self.add_error('code', '验证码错误，请重新输入。')

        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号',
                                   validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$',
                                                              '手机号格式错误'), ])

    def __init__(self, request, *args, **kwargs):
        super(SendSmsForm, self).__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """手机号校验回调函数"""
        mobile_phone = self.cleaned_data.get('mobile_phone')

        tpl = self.request.GET.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            self.add_error('mobile_phone', '短信模板错误。')
            return mobile_phone

        # 校验数据库中是否有该手机号
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            if not exists:
                self.add_error('mobile_phone', '手机号未注册。')
                return mobile_phone
        else:
            if exists:
                self.add_error('mobile_phone', '手机号已注册。')
                return mobile_phone

        # 发短信      # 手机号码可能未在电信备案
        code = random.randrange(1000, 9999)
        sms_resp = sms.send_sms_single(mobile_phone, template_id, [code, ])
        if sms_resp['result'] != 0:
            self.add_error('mobile_phone', '短信发送失败, {}。'.format(sms_resp['errmsg']))
            return mobile_phone

        # 验证码写入 Redis (django-redis)
        conn = get_redis_connection()
        conn.set(mobile_phone, code, ex=60)

        return mobile_phone


class LoginSmsForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(label='手机号',
                                   validators=[RegexValidator(r'(1[3|4|5|6|7|8|9])\d{9}$',
                                                              '手机号格式错误'), ])

    code = forms.CharField(label='验证码')

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if not exists:
            self.add_error('mobile_phone', '手机号未注册')
            return mobile_phone

        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data['mobile_phone']

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            self.add_error('mobile_phone', '验证码失效或未发送，请重新发送')
            return code

        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            self.add_error('code', '验证码错误')
        return code


class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label='邮箱/手机号')
    pass_word = forms.CharField(label='密码', widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='图片验证码')

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_code(self):
        """图片验证码是否正确"""
        code = self.cleaned_data['code']
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期。')

        if code.strip().upper() != session_code.upper():
            raise ValidationError('验证码输入错误')

        return code

    def clean_pass_word(self):
        pass_word = self.cleaned_data['pass_word']
        return encrypt.md5(pass_word)